<?php

namespace Tbf\Bundle\RefactorBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;

/**
 * 重新设置某个目录下面的所有类
 * 非常简单的重构,有很多特殊情况没有考虑到...
 * 暂时不考虑一切特殊情况,仅使用正则(应该使用ast对代码进行遍历的...)
 * TODO: 使用ast对代码进行遍历,正确分析文件包含的类,和修改类名
 */
class BuildNamespaceOnPathCommand extends ContainerAwareCommand
{
    protected $reference_path = null;
    protected function configure()
    {
        $this
            ->setName('tbf:refactor:build-namespace-on-path')
            ->setDescription('build namespace on path')
            ->addArgument('path')
            ->addArgument('namespace')
            ->addArgument('reference-path')
            ->addOption('force');
    }
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->reference_path = $input->getArgument('reference-path');
        if (empty($this->reference_path)) {
            $this->reference_path = $input->getArgument('path');
        }
        $path = $input->getArgument('path');
        if (empty($path)) {
            $output->writeln('require argument path');

            return -1;
        }
        $namespace = trim($input->getArgument('namespace'),'\\/');
        $finder = Finder::create()->depth('>0')->contains('class')->name('*.php')->files()->in(realpath($path));
        $to_rename_task = array();
        foreach ($finder as $file) {
            /** @var \Symfony\Component\Finder\SplFileInfo $file */

            $should_class = $namespace.'\\'.substr($file->getRelativePathname(),0,-4);
            $should_class = $this->normalizeFullClassName($should_class);
            $content = file_get_contents($file->getRealPath());
            $actual_class = $this->getFullClassNameInFile($content);
            if (empty($actual_class)) {
                $output->writeln('<warning>bad format of class name in '.$file->getRelativePathname().'</warning>');
                continue;
            }
            if ($should_class!== $actual_class) {
                $to_rename_task[] = array(
                    'path'=>$file->getRealPath(),
                    'actual'=>$actual_class,
                    'should'=>$should_class,
                );
            }
        }
        foreach ($to_rename_task as $task) {
            $output->writeln($task['actual'].' to '.$task['should']);
            if ($input->getOption('force')) {
                $this->setFileClass($task['path'],$task['should']);
                $this->renameClassReference($task['actual'],$task['should']);
            }
        }
    }
    //TODO: 还有多种定义class的方法
    protected function getFullClassNameInFile($content)
    {
        $ret = preg_match('/namespace (.*?);/i',$content,$match);
        if (empty($ret)) {
            $namespace = '';
        } else {
            $namespace = $match[1];
        }
        $short_class = $this->getShortClassNameInFile($content);
        if (empty($short_class)) {
            return null;
        }

        return $this->normalizeFullClassName($namespace.'\\'.$short_class);
    }
    protected function getShortClassNameInFile($content)
    {
        $ret = preg_match('/^(abstract )?(final )?class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/im',$content,$match);
        if (!empty($ret)) {
            return $match[3];
        }
        $ret = preg_match('/^interface ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/im',$content,$match);
        if (!empty($ret)) {
            return $match[1];
        }

        return null;
    }
    //TODO: 有多种定义class的方法
    protected function setFileClass($path,$class)
    {
        $content = file_get_contents($path);
        $namespace = $this->getNamespaceFromFullClassName($class);
        $content = preg_replace('/namespace (.*?);/i','namespace '.$namespace.';',$content);
        if (empty($content)) {
            throw new \Exception('fail preg_replace on setFileNamespace');
        }
        $should_short_class_name = $this->getShortClassNameFromFullClassName($class);
        $short_class_name = $this->getShortClassNameInFile($content);
        if ($short_class_name!==$should_short_class_name) {
            $content = preg_replace('/^(abstract )?(final )?class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/im',
                '\1\2class '.$should_short_class_name,$content,1);
        }
        $ret = file_put_contents($path,$content);
        if ($ret===false) {
            throw new \Exception('fail file_put_contents on setFileNamespace');
        }
    }
    protected function renameClassReference($from_full_class_name,$to_full_class_name)
    {
        $finder = Finder::create()->depth('>0')->contains($from_full_class_name)->in($this->reference_path);
        foreach ($finder as $file) {
            /** @var \Symfony\Component\Finder\SplFileInfo $file */
            $content = file_get_contents($file);
            $content = str_replace($from_full_class_name,$to_full_class_name,$content);
            file_put_contents($file,$content);
        }
    }
    protected function normalizeFullClassName($class_name)
    {
        return trim(str_replace('/','\\',$class_name),'\\');
    }
    protected function getNamespaceFromFullClassName($full_class_name)
    {
        $pos = strrpos($full_class_name,'\\');
        if ($pos===false) {
            return null;
        }

        return substr($full_class_name,0,$pos);
    }
    protected function getShortClassNameFromFullClassName($full_class_name)
    {
        $pos = strrpos($full_class_name,'\\');
        if ($pos===false) {
            return null;
        }

        return substr($full_class_name,$pos+1);
    }
}
